<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <p data-lake-id="u9ad1c3cb" id="u9ad1c3cb"><br></p>
  <p data-lake-id="u4c4e7da2" id="u4c4e7da2"><strong><span data-lake-id="ue478c11b" id="ue478c11b">原子性在并发编程中，和在数据库中两种不同的概念。</span></strong></p>
  <p data-lake-id="u2396722a" id="u2396722a"><span data-lake-id="u5d3eccab" id="u5d3eccab">​</span><br></p>
  <p data-lake-id="u1442113b" id="u1442113b"><span data-lake-id="u09197447" id="u09197447">在数据库中，事务的ACID中原子性指的是"要么都执行要么都回滚"。在并发编程中，原子性指的是"操作不可拆分、不被中断"。</span></p>
  <p data-lake-id="u3c4cde1a" id="u3c4cde1a"><span data-lake-id="u598d16ce" id="u598d16ce">​</span><br></p>
  <p data-lake-id="u23c8173d" id="u23c8173d"><span data-lake-id="u26db7eec" id="u26db7eec">Redis既是一个数据库，又是一个支持并发编程的系统，所以，他的原子性有两种。所以，我们需要明确清楚，在问"Lua脚本保证Redis原子性"的时候，指的到底是哪个原子性。</span></p>
  <p data-lake-id="u617ca904" id="u617ca904"><br></p>
  <p data-lake-id="uce289b79" id="uce289b79"><strong><span data-lake-id="udeb9a3d4" id="udeb9a3d4">Lua脚本可以保证原子性，因为Redis会将Lua脚本封装成一个单独的事务，而这个单独的事务会在Redis客户端运行时，由Redis服务器自行处理并完成整个事务，如果在这个进程中有其他客户端请求的时候，Redis将会把它暂存起来，等到 Lua 脚本处理完毕后，才会再把被暂存的请求恢复。</span></strong></p>
  <p data-lake-id="u512d74aa" id="u512d74aa"><strong><span data-lake-id="u228c57ae" id="u228c57ae">​</span></strong><br></p>
  <p data-lake-id="u6e6ef2f3" id="u6e6ef2f3"><span data-lake-id="u83d371a6" id="u83d371a6">这样就可以保证整个脚本是作为一个整体执行的，中间不会被其他命令插入。但是，如果命令执行过程中命令产生错误，事务是不会回滚的，将会影响后续命令的执行。<br><br></span></p>
  <p data-lake-id="uff5a2640" id="uff5a2640"><strong><span data-lake-id="u8271b46f" id="u8271b46f">也就是说，Redis保证以原子方式执行Lua脚本，但是不保证脚本中所有操作要么都执行或者都回滚。</span></strong><span data-lake-id="u0987392e" id="u0987392e"><br><br></span></p>
  <p data-lake-id="u64596cac" id="u64596cac"><span data-lake-id="ua3478fff" id="ua3478fff">那就意味着，Redis中Lua脚本的执行，可以保证并发编程中不可拆分、不被中断的这个原子性，但是没有保证数据库ACID中要么都执行要么都回滚的这个原子性。</span></p>
  <p data-lake-id="u78e06c29" id="u78e06c29"><br></p>
  <p data-lake-id="u1341939c" id="u1341939c"><br></p>
  <p data-lake-id="uc8bb59f9" id="uc8bb59f9"><span data-lake-id="u76ce7532" id="u76ce7532">​</span><br></p>
  <h1 data-lake-id="Q3BEo" id="Q3BEo"><span data-lake-id="uab6e6cd1" id="uab6e6cd1">扩展知识</span></h1>
  <p data-lake-id="u82988de4" id="u82988de4"><br></p>
  <h2 data-lake-id="bfSK2" id="bfSK2"><span data-lake-id="u91e45228" id="u91e45228">什么是Lua</span></h2>
  <p data-lake-id="u297ce34e" id="u297ce34e"><br></p>
  <p data-lake-id="uc81673aa" id="uc81673aa"><span data-lake-id="u7546de18" id="u7546de18">Lua是一种轻量小巧的脚本语言，用标准C语言编写并以源代码形式开放， 其设计目的是为了嵌入应用程序中，从而为应用程序提供灵活的扩展和定制功能。它提供了非常易于使用的语法，大量的库，以及可移植性，可以用于开发各种各样的应用。Lua是一种高效、轻便、可扩展的脚本语言。它具有语法简洁、操作易学，支持数据结构定义，支持函数式编程，支持面向对象编程，在功能上可以完全取代Shell、Perl，而在轻便性和可扩展性上又具有极大的优势。</span></p>
  <p data-lake-id="u361d8971" id="u361d8971"><strong><span data-lake-id="uf4230df2" id="uf4230df2">​</span></strong><br></p>
  <p data-lake-id="uf1349ceb" id="uf1349ceb"><strong><span data-lake-id="uef9f6965" id="uef9f6965">Lua脚本语言的优点：</span></strong></p>
  <ol list="ucda1d4c7">
   <li fid="u0015a5e9" data-lake-id="ue411e214" id="ue411e214"><strong><span data-lake-id="u9126db30" id="u9126db30">高效性：Lua脚本语言的解释器能够高效的执行脚本，它运行快速，可以实现复杂的程序功能。</span></strong></li>
   <li fid="u0015a5e9" data-lake-id="ue0721a4d" id="ue0721a4d"><strong><span data-lake-id="u3f9a21de" id="u3f9a21de">简单性：Lua脚本语言是一种小巧的编程语言，它只有一小部分的内部机制，易于学习，一直用代码可以轻松实现复杂程序功能。</span></strong></li>
   <li fid="u0015a5e9" data-lake-id="u5e0a892d" id="u5e0a892d"><strong><span data-lake-id="u8fe179ad" id="u8fe179ad">可移植性：Lua脚本语言可以在多种操作系统上运行，写好的脚本只要移植到其它系统上就可以运行，可以在不同操作系统上使用，可以节省时间和成本。</span></strong></li>
   <li fid="u0015a5e9" data-lake-id="u203d50e6" id="u203d50e6"><strong><span data-lake-id="u5cb97ace" id="u5cb97ace">灵活性：Lua脚本语言具有高度的灵活性，可以实现高效灵活的软件架构，帮助开发者更加精准的实现复杂的需求。</span></strong></li>
  </ol>
  <p data-lake-id="uc6d1493d" id="uc6d1493d"><strong><span data-lake-id="u896ba840" id="u896ba840">5.安全性：Lua脚本语言的安全性良好，可以保护开发者的代码不受恶意的攻击和恶意的破坏。</span></strong></p>
  <p data-lake-id="u7b2b0bb0" id="u7b2b0bb0"><strong><span data-lake-id="u2b3ebb08" id="u2b3ebb08">​</span></strong><br></p>
  <p data-lake-id="u5ea251d1" id="u5ea251d1"><span data-lake-id="u277bbae2" id="u277bbae2">以下就是一个Lua脚本，它实现的功能就是转账，通过lua脚本来保证原子性：</span></p>
  <pre lang="java"><code>
-- 转账操作的Lua脚本（支持指定双方账号）
local from_account = KEYS[1]
local to_account = KEYS[2]
local amount_to_transfer = tonumber(ARGV[1])

-- 检查是否有足够的金额可以转账
local from_balance = tonumber(redis.call("GET", from_account))
if from_balance and from_balance &gt;= amount_to_transfer then

    -- 执行扣除金额和增加金额的操作
    redis.call("DECRBY", from_account, amount_to_transfer)
    redis.call("INCRBY", to_account, amount_to_transfer)

    return true
else
    return false
end

</code></pre>
  <p data-lake-id="ub350f347" id="ub350f347"><strong><span data-lake-id="ufcbca527" id="ufcbca527">​</span></strong><br></p>
  <p data-lake-id="u01e51aef" id="u01e51aef"><span data-lake-id="uf2524d6a" id="uf2524d6a">例如，如果要从account:123转账给account:456，可以这样调用脚本：</span></p>
  <p data-lake-id="u83540fba" id="u83540fba"><span data-lake-id="u339ad307" id="u339ad307">​</span><br></p>
  <pre lang="java"><code>
EVAL "脚本内容" 2 account:123 account:456 100
</code></pre>
 </body>
</html>